package com.access.boot.apisdk.param.resolve;

import com.access.boot.apisdk.RequestContext;
import com.access.boot.apisdk.annotation.ValueConstants;
import com.access.boot.apisdk.util.MethodParameter;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author YangZhen
 * @date 2022/6/24 14:29
 * @since 1.0.0
 */
public abstract class AbstractNamedValueMethodArgumentResolver implements HandlerMethodArgumentResolver<RequestContext> {

    private final Map<MethodParameter, NamedValueInfo> namedValueInfoCache = new ConcurrentHashMap<>(256);

    @Override
    public void resolveArgument(RequestContext httpRequest, MethodParameter parameter, Object[] arguments) {
        NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
        String paramName = namedValueInfo.mappingName;
        paramName = paramName == null ? namedValueInfo.name : paramName;
        resolveNamedValue(httpRequest, paramName, parameter, arguments);
    }

    protected abstract void resolveNamedValue(RequestContext httpRequest, String name, MethodParameter parameter, Object[] arguments);

    private NamedValueInfo getNamedValueInfo(MethodParameter parameter) {
        NamedValueInfo namedValueInfo = this.namedValueInfoCache.get(parameter);
        if (namedValueInfo == null) {
            namedValueInfo = createNamedValueInfo(parameter);
            namedValueInfo = updateNamedValueInfo(parameter, namedValueInfo);
            this.namedValueInfoCache.put(parameter, namedValueInfo);
        }
        return namedValueInfo;
    }

    protected abstract NamedValueInfo createNamedValueInfo(MethodParameter parameter);

    private NamedValueInfo updateNamedValueInfo(MethodParameter parameter, NamedValueInfo info) {
        String name = info.name;
        if (info.name.isEmpty()) {
            name = parameter.getParameterName();
            if (name == null) {
                throw new IllegalArgumentException(
                        "Name for argument of type [" + parameter.getParameterType().getName() +
                                "] not specified, and parameter name information not found in class file either.");
            }
        }
        String defaultValue = (ValueConstants.DEFAULT_NONE.equals(info.defaultValue) ? null : info.defaultValue);
        return new NamedValueInfo(name, info.mappingName, info.required, defaultValue);
    }

    /**
     * Represents the information about a named value, including name, whether
     * it's required and a default value.
     */
    protected static class NamedValueInfo {

        private final String mappingName;

        private final String name;

        private final boolean required;

        private final String defaultValue;

        public NamedValueInfo(String name, String mappingName, boolean required, String defaultValue) {
            this.name = name;
            this.mappingName = mappingName;
            this.required = required;
            this.defaultValue = defaultValue;
        }
    }
}
